// 11_error_handling.rs
// 演示 Rust 中的错误处理机制

use std::fs::File;
use std::io::{self, Read, ErrorKind};
use std::path::Path;

// 1. 自定义错误类型
// 可以定义自己的错误类型来表示特定的错误情况
#[derive(Debug)]
enum GuessError {
    TooLow,
    TooHigh,
    InvalidFormat,
}

// 为自定义错误类型实现 std::error::Error trait
impl std::error::Error for GuessError {} // 使用默认实现

// 为自定义错误类型实现 Display trait
impl std::fmt::Display for GuessError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            GuessError::TooLow => write!(f, "猜测值太低！必须大于等于1"),
            GuessError::TooHigh => write!(f, "猜测值太高！必须小于等于100"),
            GuessError::InvalidFormat => write!(f, "无效的数字格式"),
        }
    }
}

// 2. 使用 Result 返回可能失败的函数
fn read_username_from_file(path: &Path) -> Result<String, io::Error> {
    // 方法1：直接返回 Result
    let mut f = File::open(path)?; // 使用 ? 操作符传播错误
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s)
}

// 简化版本，链式调用
fn read_username_simplified(path: &Path) -> Result<String, io::Error> {
    let mut s = String::new();
    File::open(path)?.read_to_string(&mut s)?;
    Ok(s)
}

// 更简化的版本，使用 std::fs::read_to_string
fn read_username_short(path: &Path) -> Result<String, io::Error> {
    std::fs::read_to_string(path)
}

// 3. 验证猜测值的函数
fn guess_number(s: &str) -> Result<u32, GuessError> {
    let guess: u32 = s.parse().map_err(|_| GuessError::InvalidFormat)?;
    
    if guess < 1 {
        Err(GuessError::TooLow)
    } else if guess > 100 {
        Err(GuessError::TooHigh)
    } else {
        Ok(guess)
    }
}

// 4. panic! 宏 - 用于不可恢复的错误
fn drink(input: &str) {
    if input == "毒药" {
        panic!("有人喝了毒药！");
    } else {
        println!("安全饮用: {}", input);
    }
}

// 5. 传播错误的另一种方式（不使用 ? 操作符）
fn open_file_manually(path: &Path) -> Result<File, io::Error> {
    match File::open(path) {
        Ok(file) => Ok(file),
        Err(error) => Err(error),
    }
}

// 6. 匹配特定类型的错误
fn open_file_and_handle_error(path: &Path) -> Result<File, String> {
    match File::open(path) {
        Ok(file) => Ok(file),
        Err(error) => match error.kind() {
            ErrorKind::NotFound => Err(format!("文件不存在: {:?}", path)),
            ErrorKind::PermissionDenied => Err(format!("没有权限访问文件: {:?}", path)),
            other_error => Err(format!("打开文件时出错: {:?}", other_error)),
        },
    }
}

// 7. 使用 unwrap 和 expect - 快速处理 Result
// unwrap 如果 Result 是 Ok 则返回值，如果是 Err 则 panic
// expect 类似于 unwrap，但允许指定 panic 消息
fn read_config() -> String {
    let config_path = Path::new("config.txt");
    std::fs::read_to_string(config_path)
        .expect("读取配置文件失败，请确保 config.txt 存在并且可读")
}

pub fn run() {
    println!("错误处理示例\n");
    
    // 8. 使用 panic!
    println!("=== 使用 panic! 宏 ===");
    drink("水");
    // drink("毒药"); // 取消注释这行会导致程序崩溃
    
    // 9. 处理 Result 类型
    println!("\n=== 处理 Result 类型 ===");
    let file_path = Path::new("hello.txt");
    
    // 方法1：使用 match 表达式
    match File::open(file_path) {
        Ok(file) => println!("成功打开文件: {:?}", file),
        Err(error) => println!("无法打开文件: {:?}", error),
    }
    
    // 方法2：使用 if let 简化
    if let Err(error) = File::open(file_path) {
        println!("使用 if let 捕获到错误: {:?}", error);
    }
    
    // 10. 使用自定义错误类型
    println!("\n=== 使用自定义错误类型 ===");
    let guess_result = guess_number("42");
    println!("猜测结果: {:?}", guess_result);
    
    let guess_result2 = guess_number("150");
    println!("猜测结果: {:?}", guess_result2);
    
    let guess_result3 = guess_number("abc");
    println!("猜测结果: {:?}", guess_result3);
    
    // 11. 打印错误信息
    if let Err(e) = guess_result2 {
        println!("错误信息: {}", e);
    }
    
    // 12. 使用 ? 操作符传播错误
    // 注意：main 函数默认不返回 Result，但我们可以修改它的返回类型
    // 这里我们使用 match 来处理 read_username_from_file 的结果
    println!("\n=== 使用 ? 操作符传播错误 ===");
    match read_username_from_file(file_path) {
        Ok(username) => println!("读取到的用户名: {}", username),
        Err(error) => println!("读取用户名失败: {:?}", error),
    }
    
    // 13. 匹配特定类型的错误
    println!("\n=== 匹配特定类型的错误 ===");
    match open_file_and_handle_error(file_path) {
        Ok(_) => println!("文件打开成功"),
        Err(message) => println!("{}", message),
    }
    
    // 14. 使用 unwrap 和 expect
    println!("\n=== 使用 unwrap 和 expect ===");
    // 注意：在实际应用中，应谨慎使用 unwrap 和 expect，因为它们会在错误时导致程序崩溃
    // 这里为了演示目的，我们用注释掉的方式展示
    
    // let content = std::fs::read_to_string("hello.txt").unwrap();
    // println!("文件内容: {}", content);
    
    // 15. 错误处理最佳实践
    println!("\n=== 错误处理最佳实践 ===");
    println!("1. 对于可恢复的错误，使用 Result<T, E> 类型");
    println!("2. 对于不可恢复的错误，使用 panic!");
    println!("3. 使用 ? 操作符简化错误传播");
    println!("4. 为库定义有意义的自定义错误类型");
    println!("5. 避免过度使用 unwrap 和 expect");
    println!("6. 在适当的层次处理错误，提供有意义的错误信息");
    
    // 16. 为了完整起见，这里展示如何修改 main 函数以返回 Result
    // fn main() -> Result<(), Box<dyn std::error::Error>> {
    //     let content = std::fs::read_to_string("hello.txt")?;
    //     println!("{}", content);
    //     Ok(())
    // }
}